package com.hqyj.javaSpringCloudAccount.service.impl;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.Optional;

import com.hqyj.javaSpringCloudAccount.dao.UserRoleDao;
import com.hqyj.javaSpringCloudAccount.service.TestFeignClient;
import com.hqyj.javaSpringCloudAccount.service.UserService;
import com.hqyj.entity.account.User;
import com.hqyj.entity.account.UserRole;
import com.hqyj.entity.common.Result;
import com.hqyj.entity.common.SearchBean;
import com.hqyj.entity.test.City;
import com.hqyj.javaSpringCloudAccount.dao.UserDao;
import com.hqyj.javaSpringCloudAccount.entity.UserVo;
import com.hqyj.javaSpringCloudAccount.util.MD5Util;
import io.github.resilience4j.circuitbreaker.CircuitBreaker;
import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig;
import io.vavr.control.Try;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

/**
 * @Description: User Service Impl
 * @author: HymanHu
 * @date: 2021年2月21日
 */
@Service
public class UserServiceImpl implements UserService {
	
	@Autowired
	private UserDao userDao;
	@Autowired
	private UserRoleDao userRoleDao;
	@Autowired
	private RestTemplate restTemplate;
	@Autowired
	private TestFeignClient testFeignClient;

	@Override
	public User getUserByUserNameAndPassword(String userName, String password) {
		return userDao.getUserByUserNameAndPassword(userName, MD5Util.getMD5(password));
	}

	@Override
	public User getUserByUserName(String userName) {
		List<User> users = Optional
				.ofNullable(userDao.getUserByUserName(userName))
				.orElse(Collections.emptyList());
		return users.isEmpty() ? null : users.get(0);
	}

	@Override
	public Result<User> login(User user) {
		User userTemp = userDao.getUserByUserNameAndPassword(user.getUserName(),
				MD5Util.getMD5(user.getPassword()));
		if (userTemp == null) {
			return new Result<User>(Result.ResultCode.FAILED.code, "Failed");
		}

		// TODO

		// 返回 result
		return new Result<User>(Result.ResultCode.SUCCESS.code, "Success",
				userTemp);
	}

	@Override
	public void logout() {
		// TODO
	}

	@Override
	@Transactional
	public Result<User> insertUser(User user) {
		List<User> users = Optional
				.ofNullable(userDao.getUserByUserNameOrEmail(user.getEmail(), user.getUserName()))
				.orElse(Collections.emptyList());
		if (users.size() > 0) {
			return new Result<User>(Result.ResultCode.FAILED.code, "User Name or email is repeat.");
		}
		
		user.setCreateDate(LocalDateTime.now());
		user.setPassword(MD5Util.getMD5(user.getPassword()));
		userDao.insertUser(user);
		if (user.getRoles() != null) {
			user.getRoles().stream()
				.forEach(item -> {userRoleDao.insertUserRole(new UserRole(user.getId(), item.getId()));});
		}
		return new Result<User>(Result.ResultCode.SUCCESS.code, "Insert success", user);
	}

	@Override
	@Transactional
	public Result<User> updateUser(User user) {
		List<User> users = Optional
				.ofNullable(userDao.getUserByUserNameOrEmail(user.getEmail(), user.getUserName()))
				.orElse(Collections.emptyList());
		if (users.stream().filter(item -> item.getId() != user.getId()).count() > 0) {
			return new Result<User>(Result.ResultCode.FAILED.code, "User Name or email is repeat.");
		}
		
		userDao.updateUser(user);
		if (user.getRoles() != null && !user.getRoles().isEmpty()) {
			userRoleDao.deleteUserRoleByUserId(user.getId());
			user.getRoles().stream()
				.forEach(item -> {userRoleDao.insertUserRole(new UserRole(user.getId(), item.getId()));});
		}
		
		return new Result<User>(Result.ResultCode.SUCCESS.code, "Update success", user);
	}

	@Override
	public User getUserById(int id) {
		return userDao.getUserById(id);
	}

	@Override
	@Transactional
	public Result<Object> deleteUserById(int id) {
		userDao.deleteUserById(id);
		userRoleDao.deleteUserRoleByUserId(id);
		return new Result<Object>(Result.ResultCode.SUCCESS.code, "Delete success");
	}

	@Override
	public PageInfo<User> getUsersBySearchBean(SearchBean searchBean) {
		searchBean.initSearchBean();
		PageHelper.startPage(searchBean.getCurrentPage(), searchBean.getPageSize());
		return new PageInfo<User>(Optional
				.ofNullable(userDao.getUsersBySearchVo(searchBean))
				.orElse(Collections.emptyList()));
	}

	@Override
	public List<User> getUsersByKeyWord(String keyWord) {
		SearchBean searchBean = new SearchBean();
		searchBean.setKeyword(keyWord);
		searchBean.initSearchBean();
		return Optional.ofNullable(userDao.getUsersBySearchVo(searchBean)).orElse(Collections.emptyList());
	}

	@Override
	public UserVo getUserVoById(int id) {
		UserVo userVo = new UserVo();
		User user = userDao.getUserById(id);
		BeanUtils.copyProperties(user, userVo);

//		City city = restTemplate.getForObject(
//				"http://CLIENT-TEST/api/test/city/{cityId}",
//				City.class, 1890);
//		userVo.setCity(city);


		/*
		 * - 熔断器策略配置
		 * - 生产者抛出异常或停掉的情况，都会返回默认值
		 */
		CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom()
				.failureRateThreshold(50)
				.waitDurationInOpenState(Duration.ofMillis(1000))
				.build();
		CircuitBreaker circuitBreaker = CircuitBreaker.of("circuitBreakerPloy", circuitBreakerConfig);
//		Try<City> circuitBreakerSupplier = Try.ofSupplier(CircuitBreaker.decorateSupplier(
//				circuitBreaker,
//				() -> restTemplate.getForObject("http://CLIENT-TEST/api/test/city/{cityId}", City.class, 1890)
//		)).recover(Exception.class, new City());
		Try<City> circuitBreakerSupplier = Try.ofSupplier(CircuitBreaker.decorateSupplier(
				circuitBreaker,
				() -> testFeignClient.getCityByCityId(1890)
		)).recover(Exception.class, new City());
		userVo.setCity(circuitBreakerSupplier.get());

		return userVo;
	}
}
